﻿using FastData;
using FastData.Caching;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;


public static class Extension
{
    public static string GetName(this Enum obj)
    {
        return Enum.GetName(obj.GetType(), obj);
    }

    public static bool IsDBNull(this object obj)
    {
        return obj == DBNull.Value;
    }

    public static object IfDBNull(this object obj, object value)
    {
        return obj == DBNull.Value ? value : obj;
    }

    public static IQueryable<TSource> Cache<TSource>(this IQueryable<TSource> source, TimeSpan slidingExpiration)
    {
        return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[1]
            {
                typeof (TSource)
            }), new Expression[2] { source.Expression, Expression.Constant(slidingExpiration) }));
    }

    public static IQueryable<TSource> Cache<TSource>(this IQueryable<TSource> source, DateTime absoluteExpiration)
    {
        return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[1]
            {
                typeof (TSource)
            }), new Expression[2] { source.Expression, Expression.Constant(absoluteExpiration) }));
    }

    public static IQueryable<TSource> CacheDependency<TSource>(this IQueryable<TSource> source, TableDependency tableDependency)
    {
        return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[1]
            {
                typeof (TSource)
            }), new Expression[2] { source.Expression, Expression.Constant(tableDependency) }));
    }

    public static IQueryable<TSource> CacheDependency<TSource>(this IQueryable<TSource> source, string tableName)
    {
        return source.CacheDependency(new TableDependency(tableName));
    }

    public static async Task<IEnumerable<T>> ToListAsync<T>(this IQueryable<T> source)
    {
        var selectQuery = source as SelectQuery<T>;
        if (selectQuery == null)
        {
            throw new InvalidOperationException("The source IQueryable isn't SelectQuery<" + typeof(T) + ">.");
        }
        return await selectQuery.ExecuteAsync();
    }

    internal static string GetString(this ExpressionType type)
    {
        switch (type)
        {
            case ExpressionType.Add:
                break;
            case ExpressionType.AddAssign:
                break;
            case ExpressionType.AddAssignChecked:
                break;
            case ExpressionType.AddChecked:
                break;
            case ExpressionType.And:
                break;
            case ExpressionType.AndAlso:
                return " and ";
            case ExpressionType.AndAssign:
                break;
            case ExpressionType.ArrayIndex:
                break;
            case ExpressionType.ArrayLength:
                break;
            case ExpressionType.Assign:
                break;
            case ExpressionType.Block:
                break;
            case ExpressionType.Call:
                break;
            case ExpressionType.Coalesce:
                break;
            case ExpressionType.Conditional:
                break;
            case ExpressionType.Constant:
                break;
            case ExpressionType.Convert:
                break;
            case ExpressionType.ConvertChecked:
                break;
            case ExpressionType.DebugInfo:
                break;
            case ExpressionType.Decrement:
                break;
            case ExpressionType.Default:
                break;
            case ExpressionType.Divide:
                break;
            case ExpressionType.DivideAssign:
                break;
            case ExpressionType.Dynamic:
                break;
            case ExpressionType.Equal:
                return "=";
            case ExpressionType.ExclusiveOr:
                break;
            case ExpressionType.ExclusiveOrAssign:
                break;
            case ExpressionType.Extension:
                break;
            case ExpressionType.Goto:
                break;
            case ExpressionType.GreaterThan:
                return " > ";
            case ExpressionType.GreaterThanOrEqual:
                return " >= ";
            case ExpressionType.Increment:
                break;
            case ExpressionType.Index:
                break;
            case ExpressionType.Invoke:
                break;
            case ExpressionType.IsFalse:
                break;
            case ExpressionType.IsTrue:
                break;
            case ExpressionType.Label:
                break;
            case ExpressionType.Lambda:
                break;
            case ExpressionType.LeftShift:
                break;
            case ExpressionType.LeftShiftAssign:
                break;
            case ExpressionType.LessThan:
                return " < ";
            case ExpressionType.LessThanOrEqual:
                return " <= ";
            case ExpressionType.ListInit:
                break;
            case ExpressionType.Loop:
                break;
            case ExpressionType.MemberAccess:
                break;
            case ExpressionType.MemberInit:
                break;
            case ExpressionType.Modulo:
                break;
            case ExpressionType.ModuloAssign:
                break;
            case ExpressionType.Multiply:
                break;
            case ExpressionType.MultiplyAssign:
                break;
            case ExpressionType.MultiplyAssignChecked:
                break;
            case ExpressionType.MultiplyChecked:
                break;
            case ExpressionType.Negate:
                break;
            case ExpressionType.NegateChecked:
                break;
            case ExpressionType.New:
                break;
            case ExpressionType.NewArrayBounds:
                break;
            case ExpressionType.NewArrayInit:
                break;
            case ExpressionType.Not:
                break;
            case ExpressionType.NotEqual:
                return " <> ";
            case ExpressionType.OnesComplement:
                break;
            case ExpressionType.Or:
                break;
            case ExpressionType.OrAssign:
                break;
            case ExpressionType.OrElse:
                return " OR ";
            case ExpressionType.Parameter:
                break;
            case ExpressionType.PostDecrementAssign:
                break;
            case ExpressionType.PostIncrementAssign:
                break;
            case ExpressionType.Power:
                break;
            case ExpressionType.PowerAssign:
                break;
            case ExpressionType.PreDecrementAssign:
                break;
            case ExpressionType.PreIncrementAssign:
                break;
            case ExpressionType.Quote:
                break;
            case ExpressionType.RightShift:
                break;
            case ExpressionType.RightShiftAssign:
                break;
            case ExpressionType.RuntimeVariables:
                break;
            case ExpressionType.Subtract:
                break;
            case ExpressionType.SubtractAssign:
                break;
            case ExpressionType.SubtractAssignChecked:
                break;
            case ExpressionType.SubtractChecked:
                break;
            case ExpressionType.Switch:
                break;
            case ExpressionType.Throw:
                break;
            case ExpressionType.Try:
                break;
            case ExpressionType.TypeAs:
                break;
            case ExpressionType.TypeEqual:
                break;
            case ExpressionType.TypeIs:
                break;
            case ExpressionType.UnaryPlus:
                break;
            case ExpressionType.Unbox:
                break;
            default:
                break;
        }
        return "?";
    }

    internal static Type GetElementType(this Type seqType)
    {
        Type ienum = seqType.FindIEnumerable();
        if (ienum == null) return seqType;
        return ienum.GetGenericArguments()[0];
    }

    internal static Type FindIEnumerable(this Type seqType)
    {
        if (seqType == null || seqType == typeof(string))
            return null;
        if (seqType.IsArray)
            return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
        if (seqType.IsGenericType)
        {
            foreach (Type arg in seqType.GetGenericArguments())
            {
                Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
                if (ienum.IsAssignableFrom(seqType))
                {
                    return ienum;
                }
            }
        }
        Type[] ifaces = seqType.GetInterfaces();
        if (ifaces != null && ifaces.Length > 0)
        {
            foreach (Type iface in ifaces)
            {
                Type ienum = FindIEnumerable(iface);
                if (ienum != null) return ienum;
            }
        }
        if (seqType.BaseType != null && seqType.BaseType != typeof(object))
        {
            return FindIEnumerable(seqType.BaseType);
        }
        return null;
    }

    internal static bool IsNullableType(this Type type)
    {
        return type.IsGenericType && (type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
    }

    public static string GetTableName(this Type type)
    {
        TableAttribute attribute = type.GetCustomAttribute<TableAttribute>(true);
        return (attribute == null ? type.Name : attribute.Name);
    }

    public static string GetDescription(this PropertyInfo prop)
    {
        DescriptionAttribute attribute = prop.GetCustomAttribute<DescriptionAttribute>(true);
        return (attribute == null ? prop.Name : attribute.Description);
    }

    internal static string GetMemberName(this Expression expression)
    {
        return (expression as MemberExpression).Member.Name;
    }

    public static IEnumerable<PropertyInfo> GetDataProperties(this Type type)
    {
        return type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(prop => prop.CanWrite && prop.PropertyType.IsDataType() && !prop.NotMapped());
    }

    internal static bool IsDataType(this Type type)
    {
        return type.IsValueType || type == typeof(String);
    }

    public static bool IsKey(this PropertyInfo prop)
    {
        return prop.GetCustomAttributes<KeyAttribute>(true).Any();
    }

    public static bool NotMapped(this PropertyInfo prop)
    {
        return prop.GetCustomAttributes<NotMappedAttribute>(true).Any();
    }
    
    public static bool IsRequired(this PropertyInfo prop)
    {
        return prop.GetCustomAttributes<RequiredAttribute>(true).Any();
    }

    internal static string Take(this StringBuilder stringBuilder)
    {
        string result = stringBuilder.ToString();
        stringBuilder.Clear();
        return result;
    }

}

